package com.atguigu.tingshu.payment.service.impl;

import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.payment.PaymentInfo;
import com.atguigu.tingshu.payment.config.WxPayV3Config;
import com.atguigu.tingshu.payment.service.PaymentInfoService;
import com.atguigu.tingshu.payment.service.WxPayService;
import com.atguigu.tingshu.payment.util.PayUtil;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.model.TransactionAmount;
import io.seata.spring.annotation.GlobalTransactional;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService {

    @Autowired
    private PaymentInfoService paymentInfoService;

    //调用微信支付获取小程序端所需参数用于拉起微信支付app

    @Autowired
    private JsapiServiceExtension jsapiServiceExtension;

    @Autowired
    private WxPayV3Config wxPayV3Config;

    /**
     * 调用微信支付获取 调起支付所需的所有参数，用于小程序端调起支付
     *
     * @param paymentType
     * @param orderNo
     * @return
     */
    @Override
    public Map<String, String> getWxPayParams(String paymentType, String orderNo) {
        try {
            Long userId = AuthContextHolder.getUserId();
            //1.先保存本地交易记录对象
            PaymentInfo paymentInfo = paymentInfoService.savePaymentInfo(paymentType, orderNo, userId);
            if (SystemConstant.PAYMENT_STATUS_PAID.equals(paymentInfo.getPaymentStatus())) {
                throw new GuiguException(211, "该交易已支付！");
            }
            //2.对接微信获取小程序端调起支付所需参数
            //2.1 预下单请求对象
            PrepayRequest prepayRequest = new PrepayRequest();
            Amount amount = new Amount();
            //微信支付金额单位：分
            amount.setTotal(1);
            prepayRequest.setAmount(amount);
            prepayRequest.setAppid(wxPayV3Config.getAppid());
            prepayRequest.setMchid(wxPayV3Config.getMerchantId());
            prepayRequest.setDescription(paymentInfo.getContent());
            prepayRequest.setNotifyUrl(wxPayV3Config.getNotifyUrl());
            prepayRequest.setOutTradeNo(orderNo);
            //TODO 开发阶段指定付款人信息wxopenId 需要将OpenID改为自己的
            Payer payer = new Payer();
            payer.setOpenid("odo3j4qp-wC3HVq9Z_D9C0cOr0Zs");
            prepayRequest.setPayer(payer);
            //2.2 调用获取微信支付所需参数
            PrepayWithRequestPaymentResponse response = jsapiServiceExtension.prepayWithRequestPayment(prepayRequest);
            if (response != null) {
                Map<String, String> map = new HashMap<>();
                map.put("timeStamp", response.getTimeStamp());
                map.put("package", response.getPackageVal());
                map.put("paySign", response.getPaySign());
                map.put("signType", response.getSignType());
                map.put("nonceStr", response.getNonceStr());
                return map;
            }
            return null;
        } catch (GuiguException e) {
            log.error("[支付服务]获取微信支付参数异常：{}", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 查询交易支付状态(根据商户订单号查询交易状态)
     *
     * @param orderNo
     * @return
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public Boolean queryPayStatus(String orderNo) {
        // 课上代码：查询微信交易状态
        /*try {
            //1.构建根据商户订单号查询交易请求对象
            QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
            request.setMchid(wxPayV3Config.getMerchantId());
            request.setOutTradeNo(orderNo);
            //2.调用wxPay业务对象查询订单交易对象得到支付状态
            Transaction transaction = jsapiServiceExtension.queryOrderByOutTradeNo(request);
            if (transaction != null) {
                //获取交易状态
                Transaction.TradeStateEnum tradeState = transaction.getTradeState();
                if (Transaction.TradeStateEnum.SUCCESS == tradeState) {
                    return true;
                }
            }
        } catch (Exception e) {
            log.error("[支付服务]查询订单：{}，支付交易状态异常：{}", orderNo, e);
            throw new RuntimeException(e);
        }
        return false;*/
        //TODO 约定默认客户端微信支付成功 默认返回true
        //更新本地交易状态及后续业务处理
        Boolean flag = false;
        try {
            Transaction transaction = new Transaction();
            transaction.setOutTradeNo(orderNo);
            //随机产生
            transaction.setTransactionId(IdUtil.getSnowflakeNextIdStr());
            TransactionAmount transactionAmount = new TransactionAmount();
            transactionAmount.setTotal(1);
            transactionAmount.setPayerTotal(1);
            transaction.setAmount(transactionAmount);
            paymentInfoService.updatePaymentInfoSuccess(transaction);
            flag = true;
        } catch (Exception e) {
            flag = false;
            throw new RuntimeException(e);
        } finally {
            return flag;
        }
    }


    @Autowired
    private RSAAutoCertificateConfig rsaAutoCertificateConfig;


    /**
     * 处理微信异步回调
     *
     * @param request
     * @return
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public Map<String, String> notifySuccess(HttpServletRequest request) {
        try {
            //1.验证签名-避免出现虚假通知

            //1.1.尝试获取请求头中信息（进需要请求头中）
            String wechatPaySerial = request.getHeader("Wechatpay-Serial");  //签名
            String nonce = request.getHeader("Wechatpay-Nonce");  //签名中的随机数
            String timestamp = request.getHeader("Wechatpay-Timestamp"); //时间戳
            String signature = request.getHeader("Wechatpay-Signature"); //签名类型
            String signatureType = request.getHeader("Wechatpay-Signature-Type"); //签名类型
            log.info("wechatPaySerial：{}", wechatPaySerial);
            log.info("nonce：{}", nonce);
            log.info("timestamp：{}", timestamp);
            log.info("signature：{}", signature);
            log.info("signatureType：{}", signatureType);


            String requestBody = PayUtil.readData(request);
            //基于获取头信息-构造 RequestParam
            RequestParam requestParam = new RequestParam.Builder()
                    .serialNumber(wechatPaySerial)
                    .nonce(nonce)
                    .signature(signature)
                    .timestamp(timestamp)
                    .body(requestBody)
                    .build();
            //2.NotificationParser用于真正验签
            NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);

            //3.支付通知回调为例，验签、解密并转换成 Transaction
            Transaction transaction = parser.parse(requestParam, Transaction.class);
            //4.判断交易结果 判断支付结果及支付金额
            if (transaction != null) {
                if (transaction.getTradeState() == Transaction.TradeStateEnum.SUCCESS) {
                    //TODO 上线后改为本地交易记录金额 测试：使用1（产生微信支付交易金额：1）
                    if (transaction.getAmount().getTotal().intValue() == 1) {
                        //5.触发更新本地交易记录及后续业务
                        paymentInfoService.updatePaymentInfoSuccess(transaction);
                        //6.响应正确应答
                        Map<String, String> map = new HashMap<>();
                        map.put("code", "SUCCESS");
                        map.put("message", "SUCCESS");
                        return map;
                    }
                }
            }
        } catch (Exception e) {
            //捕获到异常全局事务回滚，微信再次按照一定频率通知商户端
            log.error("[支付系统]异步回调异常：{}", e);
            throw new RuntimeException(e);
        }
        return null;
    }
}
